package com.lxw.multidb.modules.file;

import cn.tannn.cat.file.sdk.core.minio.MinioUtils;
import cn.tannn.jdevelops.files.sdk.constants.PropertiesId;
import io.minio.ListObjectsArgs;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.ZonedDateTime;
import java.util.*;

/**
 * MinIO目录树服务
 * <p>
 * 核心功能：
 * - 加载指定路径下的直接子节点（一级目录 + 一级文件）
 * - 支持灵活的过滤和排序配置
 * - 确保资源正确释放，无连接泄漏
 * <p>
 * 设计特点：
 * - 使用delimiter="/"获取一级子节点
 * - 完全遍历Iterable以释放HTTP连接
 * - 支持目录/文件类型、扩展名、名称关键词过滤
 * - 支持按名称/大小/时间排序
 *
 * @author lxw
 * @version 3.3
 * @date 2025-11-06
 */
@Slf4j
@Service
public class MinioTreeService {

    private static final String PATH_SEPARATOR = "/";

    /**
     * 加载指定路径下的直接子节点
     * <p>
     * 返回指定prefix下的一级子目录和一级文件，自动应用过滤和排序规则
     *
     * @param bucketName 桶名称，不能为空
     * @param prefix     前缀路径（空字符串表示根目录）
     * @param options    过滤和排序选项，不能为null
     * @return 子节点列表（已过滤、已排序）
     */
    public List<TreeNode> loadChildren(String bucketName, String prefix, TreeOptions options) {
        log.info("加载子节点 - 桶: {}, 路径: '{}', 配置: {}", bucketName, prefix, options);

        // 验证配置
        options.validate();

        // 规范化路径
        prefix = TreeServiceUtils.normalizePrefix(prefix);

        // 查询并处理结果
        List<TreeNode> children = queryAndProcessResults(bucketName, prefix, options);
        log.info("加载完成 - 路径: '{}', 子节点数: {}", prefix, children.size());
        // debug日志输出目录和文件数量
        log.debug("其中 ({}个目录, {}个文件)",
                children.stream().filter(TreeNode::isDirectory).count(),
                children.stream().filter(node -> !node.isDirectory()).count());

        return children;
    }

    // ==================== 核心查询处理 ====================

    /**
     * 查询MinIO并处理结果
     * <p>
     * 流程：查询 → 遍历 → 去重 → 分类处理 → 过滤 → 排序
     * <p>
     * 注意：必须完全遍历Iterable以释放HTTP连接
     */
    private List<TreeNode> queryAndProcessResults(String bucketName, String prefix, TreeOptions options) {
        // 构建查询参数
        ListObjectsArgs listArgs = ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(prefix)
                .delimiter(PATH_SEPARATOR)
                .build();

        List<TreeNode> children = new ArrayList<>(256);
        // 防御性去重（理论上MinIO不会返回重复，但保留作为安全检查）
        Set<String> processedPaths = new HashSet<>();

        try {
            MinioClient minioClient = MinioUtils.getMinioClient(PropertiesId.MINIO_ID);
            Iterable<Result<Item>> results = minioClient.listObjects(listArgs);

            // 完全遍历results（不能提前break，确保连接释放）
            for (Result<Item> result : results) {
                try {
                    Item item = result.get();
                    if (item == null) continue;

                    String objectPath = item.objectName();

                    // 防御性去重检查
                    if (processedPaths.contains(objectPath)) {
                        log.debug("跳过重复对象: {}", objectPath);
                        continue;
                    }
                    processedPaths.add(objectPath);

                    // 分类处理：目录 or 文件
                    TreeNode node = item.isDir()
                            ? processDirectory(item, prefix, options)
                            : processFile(item, prefix, options);

                    if (node != null) {
                        children.add(node);
                    }

                } catch (Exception e) {
                    log.warn("处理对象失败: {}, 继续处理", e.getMessage());
                }
            }

        } catch (Exception e) {
            log.error("MinIO查询失败 - 桶: {}, 前缀: '{}', 错误: {}", bucketName, prefix, e.getMessage(), e);
            return new ArrayList<>();
        }

        // 大量子节点预警
        if (children.size() > 5000) {
            log.warn("检测到大量子节点({})，可能影响性能", children.size());
        }

        // 排序
        if (!children.isEmpty()) {
            TreeServiceUtils.sortChildren(children, options);
        }

        return children;
    }

    // ==================== 目录和文件处理 ====================

    /**
     * 处理目录对象
     */
    private TreeNode processDirectory(Item item, String prefix, TreeOptions options) {
        String dirName = extractName(item.objectName(), prefix);
        if (dirName == null || dirName.isEmpty()) return null;

        log.debug("发现目录: {}", dirName);

        // 应用过滤
        if (TreeServiceUtils.shouldFilterDirectory(dirName, options)) {
            log.debug("目录被过滤: {}", dirName);
            return null;
        }

        return new TreeNode(dirName, item.objectName(), true);
    }

    /**
     * 处理文件对象
     */
    private TreeNode processFile(Item item, String prefix, TreeOptions options) {
        String fileName = extractName(item.objectName(), prefix);
        if (fileName == null || fileName.isEmpty()) return null;

        log.debug("发现文件: {}, 大小: {} bytes", fileName, item.size());

        // 应用过滤
        if (TreeServiceUtils.shouldFilterFile(fileName, options)) {
            log.debug("文件被过滤: {}", fileName);
            return null;
        }

        return createFileNode(fileName, item);
    }

    /**
     * 创建文件节点并设置属性
     */
    private TreeNode createFileNode(String fileName, Item item) {
        TreeNode fileNode = new TreeNode(fileName, item.objectName(), false);

        // 设置文件大小
        fileNode.setSize(item.size());

        // 提取扩展名
        String extension = TreeNode.extractExtension(fileName);
        fileNode.setExtension(extension);

        // 设置修改时间
        try {
            ZonedDateTime lastModified = item.lastModified();
            if (lastModified != null) {
                fileNode.setLastModified(Date.from(lastModified.toInstant()));
            }
        } catch (Exception e) {
            log.debug("设置修改时间失败: {}", fileName);
        }

        return fileNode;
    }

    // ==================== 辅助方法 ====================

    /**
     * 从完整路径中提取当前层级的名称
     * <p>
     * 示例：
     * - "data/images/", prefix="data/" → "images"
     * - "data/file.txt", prefix="data/" → "file.txt"
     */
    private String extractName(String fullPath, String prefix) {
        if (fullPath == null || fullPath.isEmpty()) return null;

        // 无前缀时，提取最后一个/后的内容
        if (prefix == null || prefix.isEmpty()) {
            int lastSlash = fullPath.lastIndexOf(PATH_SEPARATOR);
            if (lastSlash >= 0 && lastSlash < fullPath.length() - 1) {
                return fullPath.substring(lastSlash + 1);
            }
            return fullPath;
        }

        // 验证路径匹配
        if (!fullPath.startsWith(prefix)) {
            log.warn("路径不匹配 - 完整路径: {}, 前缀: {}", fullPath, prefix);
            return fullPath;
        }

        // 提取相对路径
        String relativePath = fullPath.substring(prefix.length());
        if (relativePath.isEmpty()) return null;

        // 提取第一个/前的内容（当前层级名称）
        int firstSlash = relativePath.indexOf(PATH_SEPARATOR);
        if (firstSlash >= 0) {
            return relativePath.substring(0, firstSlash);
        }

        return relativePath;
    }
}